	export default {
		name: "uniedit",
		props: {
			modelValue: {
				type: String,
				default: ''
			},
            uploadUrl: {
                type: String,
                default: () => window.APP_CONFIG?.baseurl + 'v1/upload'
            },
            path: {
                type: String,
                default: () => 'blog'
            },
            placeholder: {
            	type: String,
            	default: '请输入内容...'
            },
            fileKey: {
            	type: String,
            	default: 'file'
            }
		},
		emits: ['update:modelValue'],
		setup(props, {emit}) {
			// 编辑器状态
			const editor = ref(null)
			const formats = ref({})
			const currentColor = ref('#000000')
			const customColor = ref('')
			const colorPickerVisible = ref(false)
			const isFocused = ref(false)
			const showPlaceholder = ref(!props.modelValue)
            const isBold = computed(() => formats.value.bold)
            const isItalic = computed(() => formats.value.italic)
            const isUnderline = computed(() => formats.value.underline)
            const alignment = computed(() => formats.value.align || 'left')
            const isHeading = ref(false);
            const gjc = ref();
            const pollId = ref(null); // 修改为null初始化
            const isAIDialogVisible = ref(false)
            const aiPrompt = ref('')
            const isAIGenerating = ref(false)
            const videoParseDialogVisible = ref(false);
            const videoUrl = ref('');
            const isParsing = ref(false);
			// 预设颜色
			const colorPresets = [
				'#000000', '#333333', '#666666', '#CCCCCC',
				'#FF4444', '#0099FF', '#00CC66', '#FFAA00',
				'#00007f', '#9900FF', '#FF66CC', '#00FFFF'
			]
			let selectionRange = null
			let lastSelection = null

			// 保存选区
            const preserveSelection = () => {
                const sel = window.getSelection();
                if (sel.rangeCount > 0) {
                    const range = sel.getRangeAt(0);
                    // 增加边界点有效性校验
                    if (editor.value.contains(range.startContainer)) {
                        lastSelection = range.cloneRange()
                        return
                    }
                }
                // 当选区无效时定位到编辑器末尾
                const newRange = document.createRange()
                if (editor.value.childNodes.length > 0) {
                    newRange.setStart(editor.value, editor.value.childNodes.length)
                } else {
                    newRange.selectNodeContents(editor.value)
                }
                newRange.collapse(false);
                lastSelection = newRange;
            }

			// 恢复选区
            const reviveSelection = () => {
                if (!lastSelection) return
                const sel = window.getSelection()
                sel.removeAllRanges()
                // 处理选区边界情况
                try {
                    sel.addRange(lastSelection.cloneRange())
                } catch (e) {
                    // 选区失效时定位到末尾
                    const range = document.createRange()
                    range.selectNodeContents(editor.value)
                    range.collapse(false)
                    sel.addRange(range)
                }
            }

			// 命令执行
			const executeCommand = (command, value = null) => {
				preserveSelection()
				reviveSelection()
				// 确保编辑器保持焦点
				editor.value.focus({preventScroll: true})
				// 特殊处理颜色命令
				if (command === 'foreColor') {
					document.execCommand('styleWithCSS', false, true)
					const success = document.execCommand('foreColor', false, value)
					if (!success) {
						const span = document.createElement('span')
						span.style.color = value
						document.getSelection().getRangeAt(0).surroundContents(span)
					}
				} else {
					document.execCommand(command, false, value)
				}
				setTimeout(() => {
					updateFormats()
					preserveSelection() // 再次保存选区
				}, 50)
			}
            
            // 设置粗体等
			const toggleFormat = (format) => executeCommand(format)
			
			// 设置对齐方式
            const setAlign = (align) => {
                const commandMap = {
                    left: 'JustifyLeft',
                    center: 'JustifyCenter',
                    right: 'JustifyRight'
                }
                executeCommand(commandMap[align])
            }
            
            // 使用常量管理字号配置
            const FONT_SIZES = {
                normal: '16px',
                heading: '24px'
            };
            
            // 响应式状态管理
            const fontSize = ref(FONT_SIZES.normal);
            
            // 基于 CSS 类名的可靠切换字号
            const toggleHeading = () => {
                preserveSelection();
                const selection = window.getSelection();
                if (!selection.rangeCount) return;
                const range = selection.getRangeAt(0);
                const isCurrentlyHeading = fontSize.value === FONT_SIZES.heading;
                
                // 创建新包裹元素
                const wrapper = document.createElement('span');
                wrapper.style.fontSize = isCurrentlyHeading ? FONT_SIZES.normal : FONT_SIZES.heading;
                
                // 提取内容并插入新元素
                const extractedContent = range.extractContents();
                wrapper.appendChild(extractedContent);
                range.insertNode(wrapper);
                
                // 清理空标签（移除了 normalize()）
                if (!wrapper.hasAttribute('style') && wrapper.childNodes.length === 1) {
                    const child = wrapper.firstChild;
                    if (child.nodeType === Node.TEXT_NODE || child.nodeName === 'SPAN') {
                        wrapper.replaceWith(child);
                    }
                }
                
                // 更新状态
                fontSize.value = isCurrentlyHeading ? FONT_SIZES.normal : FONT_SIZES.heading;
                
                // 手动设置选区到新包裹元素内部
                const newRange = document.createRange();
                newRange.selectNodeContents(wrapper);
                selection.removeAllRanges();
                selection.addRange(newRange);
                
                // 确保编辑器保持焦点
                editor.value.focus({preventScroll: true});
            };
			
			// 设置颜色
			const setTextColor = (color) => {
				if (!/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(color)) return
				const hexToRgb = hex => {
					const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
					hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b)
					const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
					return result ?
						`rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)})` : null
				}
				executeCommand('styleWithCSS', true)
				executeCommand('foreColor', hexToRgb(color)) // 使用RGB格式
				currentColor.value = color
				setTimeout(() => colorPickerVisible.value = false, 100)
			}


			// 获取对齐方式
			const getAlignment = () => {
                const sel = window.getSelection()
                if (!sel.rangeCount) return 'left'
                const anchorNode = sel.anchorNode
                const element = anchorNode.nodeType === 3 ? anchorNode.parentElement : anchorNode
                return window.getComputedStyle(element).textAlign || 'left'
			}

			// 更新格式状态
			const updateFormats = ()=> {
				formats.value = {
					bold: document.queryCommandState('bold'),
					italic: document.queryCommandState('italic'),
					underline: document.queryCommandState('underline'),
					align: getAlignment(),
					fontSize: document.queryCommandValue('fontSize')
				}
			}

			// 处理输入
            const handleInput = _.debounce((e) => {
                const newHtml = e.target.innerHTML
                showPlaceholder.value = !newHtml
                // 当内容确实变化时才触发更新
                if (newHtml !== props.modelValue) {
                    emit('update:modelValue', newHtml)
                }
            },100)

			// 图片插入
            const insertImage = async () => {
                preserveSelection() // 保存当前选区状态
                editor.value.focus({ preventScroll: true }) // 保持焦点
                const input = document.createElement('input')
                input.type = 'file'
                input.accept = 'image/*'
                input.onchange = async (e) => {
                    const file = e.target.files[0]
                    if (!file) return
                    if (!file.type.startsWith('image/')) {
                        ElMessage.error('请选择图片文件')
                        return
                    }
                    try {
                        const formData = new FormData()
                        formData.append(props.fileKey, file)
                        formData.append('name', 'file')
                        formData.append('path', props.path)
                        const { data } = await axios.post(props.uploadUrl, formData, {
                            headers: { 'Content-Type': 'multipart/form-data' }
                        })
                        // 恢复选区
                        reviveSelection()
                        editor.value.focus({ preventScroll: true })
                        
                        // 确保选区有效
                        const sel = window.getSelection()
                        if (sel.rangeCount === 0) {
                            // 无选区时插入到编辑器末尾
                            const range = document.createRange()
                            range.selectNodeContents(editor.value)
                            range.collapse(false)
                            sel.addRange(range)
                        }
                        
                        // 插入图片到当前选区
                        const range = sel.getRangeAt(0)
                        const img = document.createElement('img')
                        img.src = data.data
                        img.classList.add('editor-image')
                        img.style.maxWidth = '100%'     // 限制最大宽度为容器宽度
                        img.style.height = 'auto'       // 高度自适应
                        img.style.verticalAlign = 'middle' // 垂直居中
                        img.style.cursor = 'pointer'    // 添加可点击反馈
                        range.insertNode(img)           // 在光标位置插入
                        
                        // 移动光标到图片后方
                        const newRange = document.createRange()
                        newRange.setStartAfter(img)
                        newRange.collapse(true)
                        sel.removeAllRanges()
                        sel.addRange(newRange)
                        
                        // 触发更新
                        emit('update:modelValue', editor.value.innerHTML)
                    } catch (err) {
                        ElMessage.error('图片上传失败')
                    }
                }
                input.click()
            }
            
            const handleFocus = () => {
                isFocused.value = true
                // 可选：添加聚焦样式
                editor.value.style.borderColor = '#409eff'
            }

            const handleBlur = (e) => {
                emit('update:modelValue', e.target.innerHTML)
                window.getSelection().removeAllRanges()
                updateFormats()
                e.target.style.borderColor = '#e4e7ed'
            }
            
            // 插入到编辑器中
			const insertArticleContent = async (articleData) => {
				const newContent = marked.parse(articleData);
				emit('update:modelValue', newContent)
			}
			
            const handleAIGenerate = async () => {
            	if (!aiPrompt.value.trim()) {
            		ElMessage.warning('请输入生成内容的描述')
            		return
            	}
            	try {
            		isAIGenerating.value = true
            		gjc.value = aiPrompt.value;
            		await AIConfirm(aiPrompt.value);
            		isAIDialogVisible.value = false
            	} catch (error) {
            		ElMessage.error(error.message)
            	} finally {
            		isAIGenerating.value = false
            	}
            }
            
			// AI生成文章发后端
			const AIConfirm = async (prompt) => {
				try {
					ElMessage.success('正在生成中...')
					const result = await blogformai({prompt: prompt});
					if (!result.taskId) {
						throw new Error('生成任务创建失败')
					}
					startLongPolling(result.taskId)
				} catch (error) {
					throw new Error(`生成失败: ${error.message}`)
				}
			}

            const startLongPolling = (taskId) => {
                pollId.value = setInterval(async () => {
                    try {
                        const res = await blogformai({prompt: gjc.value, pollId: taskId})
                        if (res.statusCode === 404) {
                            throw new Error('任务已过期');
                        }
                        if (res.completed) {
                            stopLongPolling();
                            insertArticleContent(res.result || props.modelValue)
                        } else {
                            const txt = res.progress || '';
                            emit('update:modelValue', txt)
                        }
                    } catch (error) {
                        stopLongPolling();
                        ElMessage.error(error.message.includes('404') ? '生成任务已超时' : error.message)
                    }
                }, 1500)
            }

			const stopLongPolling = () => {
				clearInterval(pollId.value);
				pollId.value = null;
			}
			
            // 视频解析方法
            const handleVideoParse = async () => {
                preserveSelection() // 保存当前选区状态
                const rawInput = videoUrl.value.trim();
                if (!rawInput) {
                    return ElMessage.warning('请输入视频链接');
                }
                const extractValidUrl = (input) => {
                    // 调整后的正则表达式，使用正向断言处理结尾字符
                    const urlRegex = /https?:\/\/[^\s<>{}|\\^[\]"]+(?=[\s<>{}|\\^[\]",.!?:;'`\)\]])/gi;
                    const matches = input.match(urlRegex) || [];
                    const supportedDomains = [
                        'douyin.com', 'iesdouyin.com',
                        'kuaishou.com', 'chenzhongtech.com',
                        'bilibili.com', 'b23.tv',
                        'youtube.com', 'youtu.be'
                    ];
                    // 生成域名匹配正则表达式，确保精确匹配
                    const domainRegexps = supportedDomains.map(domain => 
                        new RegExp(`^(.*\\.)?${domain.replace(/\./g, '\\.')}$`)
                    );
                    return matches.filter(url => {
                        try {
                            const hostname = new URL(url).hostname;
                            return domainRegexps.some(re => re.test(hostname));
                        } catch {
                            return false;
                        }
                    });
                }
                const getVideoType = (url) => {
                    const extension = url.split('.').pop().split(/#|\?/)[0].toLowerCase();
                    switch(extension) {
                        case 'mp4': return 'video/mp4';
                        case 'webm': return 'video/webm';
                        case 'ogg': return 'video/ogg';
                        default: return 'video/mp4'; // 默认类型
                    }
                }
                try {
                    const urls = extractValidUrl(rawInput);
                    if (urls.length === 0) throw new Error('未检测到有效链接');
                    if (urls.length > 1) throw new Error('请每次只提交一个链接');
                    const cleanedUrl = urls[0].replace(/[.,;?!)]+$/g, '');
                    const encodedUrl = encodeURI(cleanedUrl);
                    const loading = ElLoading.service({
                        lock: true,
                        text: '视频解析中（可能需要1-3分钟，请勿关闭页面）',
                        background: 'rgba(0, 0, 0, 0.7)',
                    });
                    try {
                        isParsing.value = true;
                        const response = await parseVideoUrlApi({ url: encodedUrl });
                        if (!response.url) {
                            throw new Error(response.message || '解析失败');
                        }
                        // 等待DOM更新后恢复选区
                        await nextTick();
                        reviveSelection();
                        // 处理空编辑器情况
                        if (!editor.value.innerHTML.trim()) {
                            const br = document.createElement('br');
                            editor.value.appendChild(br);
                        }
                        editor.value.focus({ preventScroll: true });
                        const sel = window.getSelection();
                        // 增强选区处理逻辑
                        let targetRange;
                        if (sel.rangeCount > 0 && editor.value.contains(sel.anchorNode)) {
                            targetRange = sel.getRangeAt(0);
                        } else {
                            // 创建新的范围到编辑器末尾
                            targetRange = document.createRange();
                            const lastChild = editor.value.lastChild;
                            if (lastChild) {
                                targetRange.setStartAfter(lastChild);
                            } else {
                                targetRange.selectNodeContents(editor.value);
                            }
                            targetRange.collapse(false);
                        }
                        // 优化视频容器结构
                        const videoWrapper = document.createElement('div');
                        videoWrapper.contentEditable = 'false';  // 防止直接编辑
                        const videoElement = document.createElement('video');
                        videoElement.controls = true;
                        videoElement.style.maxWidth = 'min(400px, 100%)';  // 默认最大宽度
                        videoElement.style.maxHeight = '70vh'; // 添加最大高度限制
                        videoElement.style.width = 'auto';     // 宽度自适应
                        videoElement.style.height = 'auto';    // 高度自适应
                        videoElement.style.margin = '0 auto';  // 水平居中
                        videoElement.style.display = 'block';  // 确保居中生效
                        const sourceElement = document.createElement('source');
                        sourceElement.src = response.url;
                        sourceElement.type = getVideoType(response.url);
                        // 优化备用提示
                        const fallbackText = document.createElement('div');
                        fallbackText.innerHTML = `浏览器不支持视频播放 <a href="${response.url}" target="_blank">下载视频</a>`;
                        fallbackText.style.padding = '8px';
                        fallbackText.style.textAlign = 'center';
                        videoElement.appendChild(sourceElement);
                        videoElement.appendChild(fallbackText);
                        videoWrapper.appendChild(videoElement);
                        // 智能插入位置处理
                        if (targetRange.collapsed) {
                            targetRange.insertNode(videoWrapper);
                            // 插入换行符确保后续输入
                            const brAfter = document.createElement('br');
                            videoWrapper.after(brAfter);
                            // 设置新选区到换行符后
                            const newRange = document.createRange();
                            newRange.selectNodeContents(brAfter);
                            newRange.collapse(true);
                            sel.removeAllRanges();
                            sel.addRange(newRange);
                        } else {
                            // 处理选区内容替换
                            targetRange.deleteContents();
                            targetRange.insertNode(videoWrapper);
                        }
                        // 同步内容更新
                        emit('update:modelValue', editor.value.innerHTML);
                        editor.value.focus();
                        videoParseDialogVisible.value = false;
                        videoUrl.value = '';
                        ElMessage.success('视频插入成功');
                    } finally {
                        loading.close();
                    }
                } catch (error) {
                    ElMessage.error(`解析失败: ${error.message}`);
                    console.error('Video parse error:', error);
                    // 自动重试提示
                    if (error.message.includes('timeout')) {
                        ElMessageBox.confirm('视频下载超时，是否重试？', '提示', {
                            confirmButtonText: '重试',
                            cancelButtonText: '取消',
                            type: 'warning'
                        }).then(() => {
                            handleVideoParse();
                        });
                    }
                } finally {
                    isParsing.value = false;
                }
            };
            
            onMounted(() => {
                if (editor.value) {
                    editor.value.innerHTML = props.modelValue || ''
                }
            })
            
            watch(() => props.modelValue, (newVal) => {
                if (editor.value && newVal !== editor.value.innerHTML) {
                    // 保存选区
                    const sel = window.getSelection()
                    const range = sel.rangeCount > 0 ? sel.getRangeAt(0) : null
                    // 更新内容
                    editor.value.innerHTML = newVal || ''
                    // 恢复选区
                    if (range) {
                        sel.removeAllRanges()
                        sel.addRange(range)
                    }
                    showPlaceholder.value = !newVal
                }
            })

			return {
                editor,
                formats,
                currentColor,
                customColor,
                colorPickerVisible,
                isFocused,
                colorPresets,
                isHeading,
                isBold,
                isItalic,
                isUnderline,
                alignment,
                handleBlur,
                handleFocus,
                toggleFormat,
                setAlign,
                toggleHeading,
                setTextColor,
                handleInput,
                insertImage,
                getAlignment,
                updateFormats,
                AIConfirm,
				startLongPolling,
				stopLongPolling,
				gjc, isAIGenerating, handleAIGenerate, 
				videoParseDialogVisible, aiPrompt, 
				videoUrl, isParsing, handleVideoParse,insertArticleContent, isAIDialogVisible
			}
		},

    template: `
		<div class="uniedit-container">
            <!-- AI生成弹窗 -->
            <el-dialog v-model="isAIDialogVisible" title="AI内容生成" width="500px" :close-on-click-modal="false">
            	<el-input v-model="aiPrompt" type="textarea" :rows="4" placeholder="请输入生成内容的描述（例如：写一篇关于人工智能的科普文章）" />
            	<template #footer>
            		<div class="dialog-footer">
            			<el-button @click="isAIDialogVisible = false">取消</el-button>
            			<el-button type="primary" :loading="isAIGenerating" @click="handleAIGenerate">
            				{{ isAIGenerating ? '生成中...' : '开始生成' }}
            			</el-button>
            		</div>
            	</template>
            </el-dialog>
    		
            <!-- 视频解析弹窗 -->
            <el-dialog v-model="videoParseDialogVisible" title="视频链接解析" width="500px" :close-on-click-modal="false">
                <el-input v-model="videoUrl" placeholder="请输入视频链接（支持抖音、快手等平台）" clearable>
                    <template #append>
                        <el-button :loading="isParsing" @click="handleVideoParse">解析</el-button>
                    </template>
                </el-input>
            </el-dialog>
			<!-- 编辑器工具栏 -->
			<div class="toolbar-group">
				<div class="toolbar-row">
					<div class="tool-group divider">
                        <div class="tool-item" @click="insertImage" @mousedown.prevent>
							<span>图片</span>
						</div>
						<div class="tool-item" @click="toggleFormat('bold')" @mousedown.prevent>
							<span>粗体</span>
						</div>
						<div class="tool-item" @click="toggleFormat('italic')" @mousedown.prevent>
							<span>斜体</span>
						</div>
						<div class="tool-item" @click="toggleFormat('underline')" @mousedown.prevent>
							<span>下划线</span>
						</div>
						<div class="tool-item" @click="toggleHeading" @mousedown.prevent>
							<span>{{ isHeading ? '正文' : '标题' }}</span>
						</div>
						<div class="tool-group divider">
                            <el-popover v-model="colorPickerVisible" placement="bottom" width="300" trigger="click">
                                <div class="color-picker">
                                    <div class="color-grid">
                                    	<div v-for="(color, index) in colorPresets" :key="index" class="color-item" :style="{ backgroundColor: color }" @mousedown.prevent @click.stop="setTextColor(color)">
                                    	</div>
                                    </div>
                                </div>
                                <template #reference>
                                	<div class="tool-item" @mousedown.prevent.stop @click.stop>
                                		<span :style="{ color: currentColor }">颜色</span>
                                	</div>
                                </template>
                            </el-popover>
                        </div>
                        <div class="tool-group divider">
                            <div class="tool-item" @click="setAlign('left')" @mousedown.prevent>
                                <span>左对齐</span>
                            </div>
                            <div class="tool-item" @click="setAlign('center')" @mousedown.prevent>
                                <span>居中</span>
                            </div>
                            <div class="tool-item" @click="setAlign('right')" @mousedown.prevent>
                                <span>右对齐</span>
                            </div>
                        </div>
                        <div class="tool-item" @click="videoParseDialogVisible = true" @mousedown.prevent>
                            <span>抖音</span>
                        </div>
                        <div class="tool-item" @click="isAIDialogVisible = true" @mousedown.prevent>
                            <span>AI生成</span>
                        </div>
                    </div>
				</div>
			</div>

			<!-- 编辑器主体 -->
			<div class="editor-content" contenteditable="true" @input="handleInput" @focus="handleFocus" @blur="handleBlur" ref="editor"></div>
		</div>

  `
}